package channelHandler

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Hex"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Json"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"github.com/dop251/goja"
	"go-fox-edge-channel-tcp-server-v2/channelEngine/channelParams"
	"go-fox-edge-channel-tcp-server-v2/channelEngine/scriptEngine"
	"go-fox-edge-channel-tcp-server-v2/channelService/reportService"
	"net"
)

type SessionHandler struct {
	Manufacturer string
	DeviceType   string
}

func (e *SessionHandler) OnMessage(ctx net.Conn, deviceKey string, data []byte) {
	// 检查：deviceKey是否已经识别出来
	if deviceKey == "" {
		return
	}

	// 取出通道参数
	channelParam := channelParams.GetChannelParam(deviceKey)
	if channelParam == nil {
		return
	}

	// 尝试响应设备
	if respond, _ := e.responds(ctx, data, deviceKey, channelParam); respond {
		return
	}

	// 没有响应设备，那么上报给设备服务去处理
	reportService.Push(deviceKey, data)
}

func (e *SessionHandler) responds(ctx net.Conn, data []byte, deviceKey string, channelParam map[string]interface{}) (ok bool, err error) {
	engines := scriptEngine.GetScriptEngines(e.Manufacturer, e.DeviceType)
	for _, engine := range engines {
		ok, err = e.respond(ctx, engine, data, deviceKey, channelParam)
		if !ok {
			continue
		}

		return ok, nil
	}

	return false, nil
}

func (e *SessionHandler) respond(ctx net.Conn, engine *goja.Runtime, data []byte, deviceKey string, channelParam map[string]interface{}) (ok bool, err error) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			message := fmt.Sprintf("exchangeService.exchange() 捕获到异常:%v", r)
			ok = false
			err = errors.New(message)
		}
	}()

	// 【步骤1】：在执行报文解码前，准备好周边数据

	// 取出函数
	decode, ok := goja.AssertFunction(engine.Get("decode"))
	if !ok {
		return false, errors.New("未定义函数：decode")
	}

	// 克隆为副本，避免影响到数据源
	channelParam = Map.Clone(channelParam)

	// 补充录入登录状态
	channelParam["login"] = channelParams.GetChannelLogin(deviceKey)

	// 填入通道参数
	engine.Set("fox_edge_param", Json.BuildJson(channelParam))
	engine.Set("fox_edge_data", Hex.EncodeToString(data))

	// 【步骤2】：执行解码函数

	// 注意：实际上只有登录报文才会执行成功，其他报文是不会成功，然后推送给设备服务去处理
	res, err := decode(goja.Undefined())
	if err != nil {
		return false, err
	}
	jsn := res.String()

	// 【步骤3】：根据解码函数返回的结果，进行相应的数据处理

	// 提取数据
	request := Json.BuildMapByJson(jsn)
	disconnect := Map.GetBool(request, "disconnect", false)

	// 记录登录状态
	if _, ok = request["login"]; ok {
		channelParams.SetChannelLogin(deviceKey, request["login"].(bool))
	}

	// 【步骤4】：在执行报文编码前，准备好周边数据

	// 取出函数
	encode, ok := goja.AssertFunction(engine.Get("encode"))
	if !ok {
		return false, errors.New("未定义函数：decode")
	}

	// 填入通道参数
	Map.Append(request, channelParam)
	engine.Set("fox_edge_param", Json.BuildJson(request))

	// 【步骤5】：执行编码函数
	res, err = encode(goja.Undefined())
	if err != nil {
		return false, err
	}
	hex := res.String()

	// 【步骤6】：返回报文给设备

	pdu := Hex.DecodeString(hex)

	// 将数据返回给设备
	ctx.Write(pdu)

	// 【步骤7】：进行收尾动作

	// 检查：是否需要主动断开连接
	if disconnect {
		ctx.Close()
	}

	return ok, nil
}
